package com.gitee.wsl.compose.chart.xy.bar


import androidx.compose.animation.core.AnimationSpec
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.gitee.wsl.common.ui.ext.color.random
import com.gitee.wsl.compose.chart.KoalaPlotTheme
import com.gitee.wsl.compose.chart.xy.XYGraphScope
import com.gitee.wsl.compose.chart.xy.bar.group.GroupedVerticalBarPlot
import com.gitee.wsl.compose.chart.xy.bar.model.DefaultBarEntry
import com.gitee.wsl.compose.chart.xy.bar.group.model.EntryToGroupedEntryListAdapter
import com.gitee.wsl.compose.chart.xy.bar.model.BarEntry
import com.gitee.wsl.compose.chart.xy.bar.model.BarPosition


fun <X, Y> BarPlot.VerticalBar(bar: (Int)-> BarSlate =  {index-> Bar.createBar { Bar.solidBar(Color.random()) } },
                               barWidth: (Int)->Float = {0.9f},
                               animationSpec: @Composable (Int)->AnimationSpec<Float> = { KoalaPlotTheme.animationSpec }
): BarPlotSlate<X,Y> = { index, data ->
    VerticalBarPlot(
        data = data,
        bar = bar(index),
        barWidth = barWidth(index),
        animationSpec = animationSpec(index)
        )
}

/**
 * A VerticalBarPlot to be used in an XYGraph and that plots a single series of data points as vertical bars.
 *
 * @param X The type of the x-axis values
 * @param xData X-axis data points for where to plot the bars on the XYGraph. The size of xData and yData must match.
 * @param yData y-axis data points for each bar. Assumes each bar starts at 0.
 * @param bar Composable function to emit a bar for each data element, given the index of the point in the data and
 * the value of the data point.
 * @param barWidth The fraction of space between adjacent x-axis bars that may be used. Must be between 0 and 1,
 * defaults to 0.9.
 */
@Composable
 fun <X> XYGraphScope<X, Float>.VerticalBarPlot(
    xData: List<X>,
    yData: List<Float>,
    modifier: Modifier = Modifier,
    bar: BarSlate,
    barWidth: Float = 0.9f,
    animationSpec: AnimationSpec<Float> = KoalaPlotTheme.animationSpec
) {
    require(xData.size == yData.size) { "xData and yData must be the same size." }
    VerticalBarPlot(
        xData.mapIndexed { index, x ->
            DefaultBarEntry(x, BarPosition(0f, yData[index]))
        },
        modifier,
        bar,
        barWidth,
        animationSpec
    )
}

/**
 * A VerticalBarPlot to be used in an XYGraph and that plots data points as vertical bars.
 *
 * @param X The type of the x-axis values
 * @param Y The type of the y-axis values
 * @param E The type of the data element holding the values for each bar
 * @param data Data points for where to plot the bars on the XYGraph
 * @param bar Composable function to emit a bar for each data element, given the index of the point in the [data].
 * @param barWidth The fraction of space between adjacent x-axis bars that may be used. Must be between 0 and 1,
 * defaults to 0.9.
 */
@Composable
 fun <X, Y, E : BarEntry<X, Y>> XYGraphScope<X, Y>.VerticalBarPlot(
    data: List<E>,
    modifier: Modifier = Modifier,
    bar: BarSlate,
    barWidth: Float = 0.9f,
    animationSpec: AnimationSpec<Float> = KoalaPlotTheme.animationSpec
) {
    val dataAdapter = remember(data) { EntryToGroupedEntryListAdapter(data) }

    // Delegate to the GroupedVerticalBarPlot - non-grouped is like grouped but with only 1 group per x-axis position
    GroupedVerticalBarPlot(
        modifier = modifier,
        dataAdapter,
        bar = { dataIndex, _, _ ->
            bar(dataIndex)
        },
        maxBarGroupWidth = barWidth,
        animationSpec = animationSpec
    )
}

/**
 * Creates a Vertical Bar Plot.
 *
 * @param defaultBar A Composable to provide the bar if not specified on an individually added item.
 * @param barWidth The fraction of space between adjacent x-axis bars that may be used. Must be between 0 and 1,
 *  defaults to 0.9.
 * @param content A block which describes the content for the plot.
 */
@Composable
 fun <X, Y> XYGraphScope<X, Y>.VerticalBarPlot(
    modifier: Modifier = Modifier,
    defaultBar: BarComposable = Bar.solidBar(Color.Blue),
    barWidth: Float = 0.9f,
    animationSpec: AnimationSpec<Float> = KoalaPlotTheme.animationSpec,
    content: VerticalBarPlotScope<X, Y>.() -> Unit
) {
    val scope = remember(content, defaultBar) { VerticalBarPlotScopeImpl<X, Y>(defaultBar) }
    val data = remember(scope) {
        scope.content()
        scope.data.values.toList()
    }

    VerticalBarPlot(
        data.map { it.first },
        modifier,
        {
            data[it].second.invoke(this)
        },
        barWidth,
        animationSpec
    )
}

